#pragma once

#include "sock.hpp"
#include <sys/epoll.h>

#define BACKLOG 20
#define NUM 128
#define BUFFER 10234

#define SOCKET_ERR 3
#define EPOLL_CREATE_ERR 4
#define EPOLL_WAIT_ERR 5
#define ACCEPT_ERR 6
#define EPOLL_CTL_DEL_ERR 7

#define RECV_SERVER_ERR 0
#define RECV_ERR 1

class EpollServer
{
public:
    EpollServer(uint16_t port, string ip = "")
        : listenfd_(-1), epollfd_(-1), port_(port), ip_(ip)
    {
        epoll_event evt;
        evt.events = 0;
        evt.data.fd = -1;
        revsArray.resize(NUM, evt);
    }

    ~EpollServer()
    {
        if (listenfd_ > STDERR_FILENO)
            close(listenfd_);

        if (epollfd_ > listenfd_)
            close(epollfd_);
    }

    void InitepollServer()
    {
        listenfd_ = Sock::Socket();
        if (listenfd_ < 0)
            exit(SOCKET_ERR);
        Sock::Bind(listenfd_, ip_, port_);
        Sock::Listen(listenfd_, BACKLOG);
    }

    void StartepollServer()
    {
        // 1.创建epoll模型 -- 在内核中构建回调机制、红黑树和就绪队列 -- 返回值为：fd
        epollfd_ = epoll_create(128);
        if (epollfd_ < 0)
        {
            cerr << "epoll_create error: " << strerror(errno) << endl;
            exit(EPOLL_CREATE_ERR);
        }

        // 2.添加需要监听的fd的特定事件到epoll模型中(本质是构建一个红黑树节点push到和红黑树中) -- "epoll_ctl"
        struct epoll_event evEnt;
        evEnt.events = EPOLLIN;    // 监听事件为读事件就绪
        evEnt.data.fd = listenfd_; // 监听的fd为listenfd_
        epoll_ctl(epollfd_, EPOLL_CTL_ADD, listenfd_, &evEnt);

        int timeout = 1000;        // 单位毫秒 -- >0是阻塞 | ==0是非阻塞 | <0是永久阻塞
        while (true)
        {
            // 等待事件就绪，就绪后回调机制的函数会获取fd和就绪事件类型，构建就绪队列节点，然后push到就绪队列中
            // epoll_wait是：获取就绪队列中全部节点(覆盖式的拷贝到revsArray中)，并且返回已经就绪的fd文件描述个数(返回值) -- 根据返回值遍历
            int n = epoll_wait(epollfd_, &revsArray[0], revsArray.size(), timeout);

            // 如果就绪队列获取的有效fd的个数大于revsArray的size大小，需要扩容，扩容后重新获取(continue)
            if (revsArray.size() < n)
            {
                epoll_event evt;
                evt.events = 0;
                evt.data.fd = -1;
                revsArray.resize(revsArray.size() * 2, evt);
                continue;
            }

            switch (n)
            {
            case 0:
                cout << "time out....... --> (时间戳: " << (unsigned long)time(nullptr) << ")" << endl;
                break;
            case -1:
                cerr << "select error: " << strerror(errno) << endl;
                exit(EPOLL_WAIT_ERR);
                break;
            default:
                // 等待读事件就绪成功
                HandlerEvents(n);
                break;
            }
        }
    }

private:
    void RecvAnyError(int flags, int sockfd, int n)
    {
        if (flags == RECV_SERVER_ERR)
            cout << "Client close, recv error: " << strerror(errno) << ", fd: "  << sockfd << endl;
        
        if (flags == RECV_ERR)
             cout << "recv error: " << strerror(errno) << ", fd: " << sockfd << endl;
        
        // 既然文件描述符都关闭了，那么该fd不用进行监听了，从它在红黑树中删除(DEL)即可 -- 先删除节点再关闭fd
        // epoll必须保证这个fd是合法的，不然会报错，必须先删除再关闭fd
        int epdel = epoll_ctl(epollfd_, EPOLL_CTL_DEL, sockfd, nullptr);
        if (epdel < 0)
        {
            cout << "epoll_ctl del RB_Tree Node error: " << strerror(errno) << endl;
            exit(EPOLL_CTL_DEL_ERR);
        }
        close(sockfd);
    }

    void EstablishMap(int serverfd, string& ClientIp, uint16_t ClientPort)
    {
        string Data = ClientIp;
        Data += ':';
        Data += std::to_string(ClientPort);

        UserNetworkData[serverfd] = Data;
    }

    void HandlerEvents(int n)
    {
        for (int i = 0; i < n; ++i)
        {
            int sockfd = revsArray[i].data.fd;        // 事件就绪的fd
            uint32_t evEnts = revsArray[i].events;    // 已经就绪的事件类型

            // 1.检测是什么事件就绪 -- 可能是读事件或写事件就绪等等 -- 读事件分为连接事件和普通读事件
            if (evEnts & EPOLLIN) // 读事件就绪
            {
                // 连接事件就绪
                if (sockfd == listenfd_)
                {
                    string ClientIp;
                    uint16_t ClientPort;
                    int serverfd = Sock::Accept(listenfd_, &ClientIp, &ClientPort); // 不会阻塞，连接事件已就绪
                    if (serverfd < 0) continue;
                    EstablishMap(serverfd, ClientIp, ClientPort); // 建立serverfd和ip:port映射关系，保存用户网络数据
                    
                    // 因为拿(accept)上来的serverfd中的接受缓冲区可能没有数据，不能立即读取(read/recv)，也要放到epoll模型中进行事件就绪等待
                    struct epoll_event evEnt;
                    evEnt.events = EPOLLIN;
                    evEnt.data.fd = serverfd;
                    epoll_ctl(epollfd_, EPOLL_CTL_ADD, serverfd, &evEnt);
                }
                else
                {
                    // 普通读事件就绪 -- 上面新添加到epoll模型中的fd就是普通读事件
                    char buffer[BUFFER];
                    auto pos = UserNetworkData.find(sockfd);
                    string UserData = pos->second;
                    
                    // 正常进程读取操作，此时不会阻塞("等待")
                    ssize_t r = recv(sockfd, buffer, BUFFER - 1, 0);
                    if (r > 0)
                    {
                        buffer[r] = '\0';
                        cout << "Client[" << UserData << "]# " << buffer;
                    }
                    else if (r == 0)
                    {
                        RecvAnyError(RECV_SERVER_ERR, sockfd, i);
                    }
                    else
                    {
                        RecvAnyError(RECV_ERR, sockfd, i);
                    }
                }
            }
            else if (evEnts & EPOLLOUT) // 写事件就绪
            {
                // ...........
            }
        }
    }

private:
    int listenfd_;
    int epollfd_;
    uint16_t port_;
    string ip_;
    vector<epoll_event> revsArray;
    unordered_map<int, string> UserNetworkData;
};